<template>
  <view class="mxp-image" :style="[cusStyle, { width, height, borderRadius, background: newBackground }]" :class="[cusClass]" @click="$emit('click')">
    <image
      v-if="data.type === 1 || data.type === 2"
      class="image"
      :draggable="draggable"
      :style="{ width, height, borderRadius, opacity: data.type === 2 ? 1 : 0 }"
      :lazyLoad="lazyLoad"
      :src="data.realSrc"
      :fadeShow="fadeShow"
      :mode="mode"
      @error="onImgError"
      @load="onImgLoad"
    ></image>
    <view v-if="data.type === 1 || data.type === 3" class="error-view" :style="{ width, height, borderRadius }">
      <slot v-if="data.type === 1" name="loading">
        <mxp-icon :size="loadingSize" :color="loadingColor" :spin="true" name="jiazai"></mxp-icon>
      </slot>
      <slot v-if="data.type === 3" name="error">
        <mxp-icon :size="errorSize" :color="errorColor" name="tupianjiazaishibai" :cusStyle="errorSize ? {} : { transform: 'scale(0.4)' }"></mxp-icon>
      </slot>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { type CSSProperties, computed, onBeforeMount, shallowReactive, watch } from 'vue';
  import type { $AllColorStr, $AllSizeUnitStr, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import MxpIcon from '../mxp-icon/mxp-icon.vue';
  import { ImgCache } from '@/mxp-ui/mxp-tool/utils/file-cache/file-cache';
  import type { ImageMode, ImageOnLoadEvent } from 'mxp-uni-types';

  export type $ImageProps = {
    /** 图片宽度 */
    width?: $AllSizeUnitStr;
    /** 图片高度 */
    height?: $AllSizeUnitStr;
    /** 错误图片路径 */
    errorSrc?: string;
    /** 鼠标长按是否能拖动图片 仅H5平台支持 */
    draggable?: boolean;
    /** 加载失败展示的背景色 */
    background?: $AllColorStr;
    /** 图片资源路径 */
    src?: string;
    /** 懒加载 */
    lazyLoad?: boolean;
    /** 是否缓存 */
    cache?: boolean;
    /** 图片显示动画效果 (仅App-nvue 2.3.4+ Android有效) */
    fadeShow?: boolean;
    /** 加载中渲染的小图片大小 */
    loadingSize?: $SizeUnitStr;
    /** 加载中图片的颜色 */
    loadingColor?: $AllColorStr;
    /** 加载失败渲染的小图片大小 */
    errorSize?: $AllSizeUnitStr;
    /** 加载失败图片的颜色 */
    errorColor?: $AllColorStr;
    /**
     * 图片裁剪、缩放的模式
     * @param scaleToFill - 不保持纵横比缩放图片，使图片的宽高完全拉伸至填满 image 元素
     * @param aspectFit - 保持纵横比缩放图片，使图片的长边能完全显示出来，可以完整地将图片显示出来
     * @param aspectFill - 保持纵横比缩放图片，只保证图片的短边能完全显示出来，图片通常只在水平或垂直方向是完整的，另一个方向将会发生截取
     * @param widthFix - 宽度不变，高度自动变化，保持原图宽高比不变
     * @param heightFix - 高度不变，宽度自动变化，保持原图宽高比不变
     * @param top - 不缩放图片，只显示图片的顶部区域
     * @param bottom - 不缩放图片，只显示图片的底部区域
     * @param center - 不缩放图片，只显示图片的中间区域
     * @param left - 不缩放图片，只显示图片的左边区域
     * @param right - 不缩放图片，只显示图片的右边区域
     * @param top - left 不缩放图片，只显示图片的左上边区域
     * @param top - right 不缩放图片，只显示图片的右上边区域
     * @param bottom - left 不缩放图片，只显示图片的左下边区域
     * @param bottom - right 不缩放图片，只显示图片的右下边区域
     */
    mode?: ImageMode;
    /** 圆角 */
    borderRadius?: string;
    /** 自定义的样式属性 */
    cusStyle?: CSSProperties;
    /** 自定义类名 */
    cusClass?: string;
  };
  defineOptions({
    name: 'mxp-image',
  });
  const props = withDefaults(defineProps<$ImageProps>(), {
    width: '80px',
    height: '80px',
    errorSrc: '',
    draggable: false,
    cache: false,
    background: '#f3f4f6',
    src: '',
    lazyLoad: true,
    fadeShow: true,
    loadingSize: '18px',
    loadingColor: '#dddddd',
    errorSize: undefined,
    errorColor: '#dddddd',
    mode: 'aspectFill',
    borderRadius: '0px',
    cusStyle: () => ({}),
    cusClass: '',
  });
  defineSlots<{
    /** 自定义加载中的图标 */
    loading?: () => void;
    /** 自定义加载失败的图片 */
    error?: () => void;
  }>();
  const emits = defineEmits<{
    (e: 'click'): void;
    (e: 'error'): void;
    (e: 'load', err: ImageOnLoadEvent): void;
  }>();

  const data = shallowReactive({
    realSrc: '',
    /** 1:加载中，2:加载成功；3:加载失败 */
    type: 1,
  });
  watch(
    () => props.src,
    (elf) => {
      if (props.src === '' && props.errorSrc) {
        data.realSrc = props.errorSrc;
      } else {
        if (props.cache) {
          handleCache(elf);
        } else {
          data.realSrc = elf;
        }
      }
    },
    { immediate: true }
  );

  const newBackground = computed(() => (data.type === 2 ? 'transparent' : props.background));
  /**
   * 处理缓存
   * @param {string} imgUrl - 网络图片地址
   */
  async function handleCache(imgUrl: string) {
    // #ifdef H5
    data.realSrc = imgUrl;
    // #endif

    // #ifndef H5
    const imgCache = new ImgCache();
    if (!imgUrl.startsWith('http')) {
      data.realSrc = imgUrl;
      return;
    }
    // 判断图片是否存在缓存
    const localUrl = await imgCache.getCache(imgUrl);
    if (localUrl) {
      // 存在缓存，直接用本地图片地址
      data.realSrc = localUrl;
    } else {
      // 不存在缓存，用网络地址
      data.realSrc = imgUrl;
      // 并且设置缓存
      imgCache.setCache(imgUrl);
    }
    // #endif
  }

  onBeforeMount(() => {
    if (props.src === '' && props.errorSrc) {
      data.realSrc = props.errorSrc;
    }
  });

  /**
   * 加载成功
   */
  function onImgLoad(e: ImageOnLoadEvent) {
    data.type = 2;
    emits('load', e);
  }

  /**
   * 加载失败
   */
  function onImgError() {
    if (props.errorSrc !== '') {
      data.type = 2;
      data.realSrc = props.errorSrc;
    } else {
      data.type = 3;
    }
    emits('error');
  }
</script>

<style lang="scss" scoped>
  .mxp-image {
    position: relative;
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    justify-content: center;
    overflow: hidden;

    .image {
      overflow: hidden;
    }

    .error-view {
      position: absolute;
      top: 0;
      left: 0;
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
  }
</style>
